#include "ndet-epsilon-determinisation.h"

using namespace std;

AutoNDE::AutoNDE()
{
  nb_etats = 0;
  nb_symbs = 0;
  nb_finaux = 0;
  initial = 0;
}

AutoNDE::AutoNDE(const AutoNDE& value)
{
    initial = value.initial;
    finaux = value.finaux;
    trans = value.trans;
    epsilon = value.epsilon;
    epsilon_clot = value.epsilon_clot;
    nb_etats = value.nb_etats;
    nb_finaux = value.nb_finaux;
    nb_symbs = value.nb_symbs;
}

AutoNDE::AutoNDE(const string& filename)
{
    ifstream myfile(filename.c_str(), ios::in);
    //un flux d'entree obtenu à partir du nom du fichier
    string line;
    // un ligne lue dans le fichier avec getline(myfile,line);
    istringstream iss;
    // flux associé à la chaine, pour lire morceau par morceau avec >> (comme cin)
    etat_t s(0), t(0);
    // deux états temporaires
    symb_t a(0);
    // un symbole temporaire

    if (myfile.is_open()){
        // la première ligne donne 'nb_etats nb_symbs nb_finaux'
        do{
            getline(myfile,line);
        } while (line.empty() || line[0]=='#');
        // on autorise les lignes de commentaires : celles qui commencent par '#'
        iss.str(line);
        if((iss >> nb_etats).fail() || (iss >> nb_symbs).fail() || (iss >> nb_finaux).fail())
            throw new std::exception();
        // la deuxième ligne donne l'état initial
        do{
            getline (myfile,line);
        } while (line.empty() || line[0]=='#');
        iss.clear();
        iss.str(line);
        if((iss >> initial).fail())
            throw new std::exception();

        // les autres lignes donnent les états finaux
        for(size_t i = 0; i < nb_finaux; i++){
            do{
                getline (myfile,line);
            } while (line.empty() || line[0]=='#');
            iss.clear();
            iss.str(line);
            if((iss >> s).fail())
                continue;
            finaux.insert(s); //remplir finaux
        }

        // on alloue les vectors à la taille connue à l'avance pour éviter les resize dynamiques
        trans.resize(nb_symbs);
        for(size_t i=0;i<nb_symbs;i++)
            trans[i].resize(nb_etats);
        epsilon_clot.resize(nb_etats);
        // lecture de la relation de transition
        while(myfile.good()){
            line.clear();
            getline (myfile,line);
            if (line.empty() && line[0]=='#')
                continue;
            iss.clear();
            iss.str(line);

            // si une des trois lectures echoue, on passe à la suite
            if((iss >> s).fail() || (iss >> a).fail() || (iss >> t).fail() || (a< ASCII_A ) || (a> ASCII_Z ))
                continue;

            //test espilon ou non
            if ((a-ASCII_A) >= nb_symbs)
                epsilon_clot[s].insert(t); //remplit epsilon_clot
            else
                trans[a-ASCII_A][s].insert(t); //remplit trans
        }
        myfile.close();

        // copie de epsilon dans epsilon_clot
        for(auto etats: epsilon_clot)
        {
            etatset_t new_etats;
            for(auto etat: etats)
                new_etats.insert(etat);
            epsilon.push_back(new_etats);
        }

        Warshall();
    }
    else {
        throw new std::exception();
    }
}

/*!
 * \brief FromFile lit le fichier en entré et remplit la structure automate avec les informations luent.
 * \param at automate à renseigner
 * \param path chemin du fichier contenant la description de l'automate
 * \return TRUE si la lecture s'est bien passée, FALSE sinon
 */
AutoNDE* FromFile(string path)
{
  if(path.empty())
    return NULL;
  try {
    return new AutoNDE(path);
  }
  catch (std::exception e) {
    // on ne peut pas ouvrir le fichier
    return (AutoNDE*)0;
  }
}

/*!
 * \brief ContientFinal Teste si l'ensemble des état passé en argument contient au moins un état final
 * \param at automate
 * \param e ensemble d'état final
 * \return VRAI si l'ensemble des état passé en argument contient au moins un état final, FALSE sinon
 */
bool AutoNDE::ContientFinal(const etatset_t& e) const{
  set<etat_t>::const_iterator first1 = finaux.begin();
  set<etat_t>::const_iterator last1 = finaux.end();
  set<etat_t>::const_iterator first2 = e.begin();
  set<etat_t>::const_iterator last2 = e.end();
  while ((first1!=last1) && (first2!=last2))
    {
      if (*first1<*first2)
	first1++;
      else{
	if (*first2<*first1)
	  first2++;
	else
	  return true;
      }
    }
  return false;
}

/*!
 * \brief Fermeture Calcul la fermeture transitive des état accessible par epsilon_clot transition à partir de l'ensemble d'états passés en arguments
 * \param at automate
 * \param e ensemble d'état à clores en entrée-sortie
 */
void AutoNDE::Fermeture(etatset_t& e)
{
    // Cette fonction clot l'ensemble d'états E={e_0, e_1, ... ,e_n} passé en
    // paramètre avec les epsilon_clot transitions
    etatset_t temp;
    for (etatset_t::const_iterator i = e.begin(); i != e.end(); i++)
    {
        temp.insert((*i));
        for(etatset_t::const_iterator j = epsilon_clot[(*i)].begin(); j != epsilon_clot[(*i)].end(); j++)
        {
            temp.insert(*j);
        }
    }
    e = temp;
}

/*!
 * \brief EstDeterministe Test si l'automate passé en argument est déterministe.
 * \param at automate
 * \return VRAI si l'automate est déterministe, FALSE sinon
 */
bool AutoNDE::EstDeterministe() const{
    int x = 0;
    for(epsilon_t::const_iterator it = epsilon_clot.begin(); it != epsilon_clot.end(); it++)
    {
        x++;
        if (!(it->size() == 0) && !(it->size() == it->count(x)))
        {
            return false;
        }
    }
    for (trans_t::const_iterator i = trans.begin(); i != trans.end(); i++)
    {
        for (vector<etatset_t>::const_iterator j = (*i).begin(); j != (*i).end(); j++)
        {
            if((*j).size() > 1)
            {
                return false;
            }
        }
    }
    return true;
}

/*!
 * \brief Warshall Calcul pour chaque état de l'automate passé en argument la fermeture transitive des états accéssible par epsilon_clote transition
 * \param at automate
 * \note Utilise l'algorithme de Warshall qui néssécite en premier lieux une convertion de la matrice de transition
 *vers un graphe de relation binaire modélisé par un tableau à deux dimension de booléens.
 */
void AutoNDE::Warshall()
{
  //Cette fonction aurai très bien put être créée par Chuck Norris
  const unsigned int n = nb_etats;
  bool** C = new bool*[n];
  bool** A = new bool*[n];

  //On ninitialise les matrices d'adjacence à false. C'est a dire que toute les relation binaire entre état sont a FAUX.
  for (unsigned int i = 0; i < n; i++) {
    C[i] = new bool[n];
    A[i] = new bool[n];
    for (unsigned int j = 0; j < n; j++) {
      C[i][j] = false;
      A[i][j] = false;
    }
  }

  //On crée un compteur pour connaitre la position de notre itérateur dans la première boucle
  int count = 0;
  //On parcours tout les état de départ de notre tableau d'epsilon_clot transition
  for(epsilon_t::const_iterator i = epsilon_clot.begin(); i != epsilon_clot.end(); i++) {
    //Pour chaque état de départ, on parcours les différents états de destination de l'espilone transition
    for (etatset_t::const_iterator j = (*i).begin(); j != (*i).end(); j++) {
      //On remplit l'état de la relation binaire de notre matrice d'adjacence comme étant VRAI
      //Nous avons donc l'index de notre état de départ qui correspond à la variable count
      //L'index de notre état de destination qui correspond à la variable (*j)
      //On définie donc la relation binaire entre l'état de départ et destination à VRAI
      //Ce qui signifie que se chemin existe.
      C[count][(unsigned int)(*j)] = true;
    }
    count++;
  }

  unsigned int i, j, k;
  for(i = 0; i < n; i++)
    for(j = 0; j < n; j++)
      A[i][j] = C[i][j];
  for(k = 0; k < n; k++)
    for(i = 0; i < n; i++)
      for(j = 0; j < n; j++)
	A[i][j] = A[i][j] || (A[i][k] && A[k][j]);

  count = 0;
  //Pour chaque état de départ
  for(epsilon_t::const_iterator i = epsilon_clot.begin(); i != epsilon_clot.end(); i++) {
    //Pour chaque relaction binaire depuis cet état de départ
    //Ce qui reviens a tester tout les état de destination
    for (j = 0; j < n; j++) {
      //Si la relation existe entre cet état de départ et cet état de destination.
      if(A[count][j]) {
	epsilon_clot[count].insert(j); //On insert le numéro d'état dans notre ensemble de destination. (peu importe s'il existe déjà).
      }
    }
    count++;
  }
}

/*!
 * \brief Delta Cette fonction prend un automate, un ensemble d'état E={e_0, e_1, ... ,e_n}
 et renvoie l'ensemble des états accessibles depuis au moins un des e_i.
 Autrement dit, c'est l'union des ensembles d'états x_i tels que x_i = {x | (e_i,c,x) in Delta}
 * \param at automate
 * \param e ensemble d'états
 * \param c symbole à lire par l'automate de type unsigned char
 * \return
 */
etatset_t AutoNDE::Delta(const etatset_t& e, symb_t c){
    etatset_t res;
    etatset_t temp = e;
    Fermeture(temp);
    for (etatset_t::const_iterator i = temp.begin(); i != temp.end(); i++)
    {
        for (etatset_t::const_iterator j = trans[c-97][(*i)].begin(); j != trans[c-97][(*i)].end(); j++)
        {
            res.insert(*j);
        }
    }
    return res;
}

/*!
 * \brief Accept Cette fonction prend en argument un automate et détermine si il accepte le mot str
 * \param at automate
 * \param str chaine de caractère de type string à lire par l'automate
 * \return VRAI si la chaine de caractère passé en argument est acceptée par l'automate, FAUX sinon
 */
bool AutoNDE::Accept(string str){
    etatset_t res;
    res.insert(initial);
    for(string::const_iterator it = str.begin(); it != str.end(); it++)
    {
        res = Delta(res, *it);
    }
    Fermeture(res);
    if(ContientFinal(res))
        return true;
    return false;
}

template<class T> ostream& operator<<(ostream& out, const set<T>& s) {
  typename set<T>::iterator first = s.begin(), last = s.end();
  out << '{';
  if(first!=last)
    out << *first++;
  for(; first != last ; ++first)
    out << ',' << (*first);
  out << '}';
  return out;
}

/*!
 * \brief operator << Surcharge de l'opérateur de flux de sortie. Aggrège une description des transition
 */
ostream& operator<<(ostream& out, const trans_t& symboles) {
    int i = 0, j = 0;
    for(trans_t::const_iterator etats = symboles.begin();
        etats != symboles.end();
        etats++)
    {
        for(vector<etatset_t>::const_iterator etats_finaux = etats->begin();
            etats_finaux != etats->end();
            etats_finaux++)
            out << "\tDelta (" << j++ << "," << (char)(i + ASCII_A) << " ) : " << (*etats_finaux) << endl;
        i++;
        j = 0;
    }
    return out;
}

/*!
 * \brief operator << Surcharge de l'opérateur de flux de sortie. Aggrège une description des ε-transition
 */
ostream& operator<<(ostream& out, const epsilon_t& etats) {
  int j = 0;
  for(epsilon_t::const_iterator etats_finaux = etats.begin();
      etats_finaux != etats.end();
      etats_finaux++)
    out << "\tDelta (" << j++ << ", ε) : " << (*etats_finaux) << endl;
  return out;
}

/*!
 * \brief operator << Surcharge de l'opérateur de flux de sortie. Aggrège une description de l'automate
 */
ostream& operator<<(ostream& out, const AutoNDE& value){
  out << "Nombre d'états : " << value.nb_etats << endl;
  out << "Nombre de symboles : " << value.nb_symbs << endl;
  out << "Transitions spontanees : " << (value.EstDeterministe() ? "NON" : "OUI") << endl;
  out << "Etat initial : " << value.initial << endl;
  out << "Etat finaux : " << value.finaux << endl;
  out << "Transitions :" << endl << value.trans ;
  if(!value.EstDeterministe())
  {
      out << "ε-Transitions :" << endl << value.epsilon;
      out << "Fermeture transitive des ε-transitions :" << endl << value.epsilon_clot;
  }
  return out;
}

/*!
 * \brief AutoNDE::ToGraph Enregistre une réprésentation de l'automate sous forme de graphe dans un fichier au format GraphWiz (*.gv)
 * \param path chemin du fichier dans lequel enregistrer le graph sans l'extension
 * \return VRAI si l'enregistremnt dans la fichier à réussi, FAUX sinon
 */
bool AutoNDE::ToGraph(string path){
    ostringstream header, foot, graph, transition, finals, unfinals, start;

    header << "digraph finite_state_machine {" << endl;
    header << "rankdir=LR;" << endl;
    header << "lsize=\"10,10\"" << endl;

    foot << "}" << endl;

    finals << "node [shape = doublecircle]; ";
    unfinals << "node [shape = circle]; ";
    start << "node [shape = point ]; q;" << endl
          << "q -> " << initial << endl;

    for(etat_t i = 0; i < nb_etats; i++)
    {
        if(finaux.find(i) == finaux.end())
            unfinals << i << " ";
        else
            finals << i << " ";
    }
    unfinals << endl;
    finals << endl;

    int i = 0, j = 0;
    for(auto etats: trans)
    {
        for(auto etats_finaux: etats)
        {
            for(auto destination: etats_finaux)
                transition << j << " -> " << destination << " [label = \"" << (char)(ASCII_A + i) << "\"];" << endl;
            j++;
        }
        i++;
        j = 0;
    }
    for(auto etat: epsilon)
    {
        for(auto destination: etat)
               transition << j << " -> " << destination << " [label = \"ε\"];" << endl;
        j++;
    }

    graph << header.str();
    graph << finals.str();
    graph << unfinals.str();
    graph << start.str();
    graph << transition.str();
    graph << foot.str();

    ofstream file(path.c_str(), ios::trunc)	;
    if(!file.fail())
    {
        file << graph.str();
        file.close();
        return true;
    }
    else
        return false;
}

/*!
 * \brief AutoNDE::Determinize Renvoie un nouvel automate
 * qui représente une version determinisée de l'automate passé en argument
 * ansi qu'une map défnissant les anciens numéro d'état vers les nouveaux
 */
pair<AutoNDE* ,map_t> AutoNDE::Determinize(){

    //Déclaration des variables
    AutoNDE* p_r = new AutoNDE(); //Automate de retour
    AutoNDE& r = *p_r; //Reference de travail

    map_t m; //map défnissant les anciens numéro d'état vers les nouveaux
    etatset_t tmp;
    queue<etatset_t> pile;

    //initialisation de l'automate
    r.nb_symbs = nb_symbs;
    r.nb_etats = 1;
    r.initial = 0;
    r.trans.resize(nb_symbs);

    //cosntruction du premier état
    etatset_t intial_clot = epsilon_clot.at(initial);
    intial_clot.insert(initial);
    if(ContientFinal(intial_clot))
        r.finaux.insert(0);
    m[intial_clot] = 0;

    //empilement du premier état
    pile.push(intial_clot);

    //déterminisation
    while (!pile.empty())
    {
        tmp = pile.front();
        pile.pop();

        if(__DEBUG) cout << "Traitement de l'état " << m[tmp] << " " <<tmp << endl;

        //pour chaque symboles
        for(etat_t i = 0; i< nb_symbs; i++)
        {
            if(__DEBUG) cout << "Quand l'état " << m[tmp] << " " <<  tmp << " lit le symbole '" << (char)(ASCII_A + i) << "'" << endl;
            etatset_t ntmp;
            //pour chaque état de destination
            for (etatset_t::const_iterator it = tmp.begin(); it != tmp.end(); it++)
            {
                //on récupère les états accesibles depuis cet état et ce symbole
                etatset_t etrans = trans[i][(*it)];
                ntmp.insert(etrans.begin(), etrans.end());
                //on ajoute leur fermeture transitive
                for(etatset_t::const_iterator it1 = ntmp.begin(); it1 != ntmp.end(); it1++)
                {
                    ntmp.insert(epsilon_clot.at((*it1)).begin(), epsilon_clot.at((*it1)).end());
                }
            }
            if(__DEBUG) cout << "Il se déplace dans la liste d'états suivante " << ntmp << endl;

            //Si cette liste d'état ne corresond à aucun état dans la pile
            if(m.find(ntmp) == m.end())
            {
                if(__DEBUG) cout << "Cette liste d'état ne corresond à aucun état dans la pile" << endl;
                if(__DEBUG) cout << "Ce nouvelle état prend la valeur " <<  r.nb_etats << (ContientFinal(ntmp) ? ", il est final" : "") << endl;

                //On test si cet état est final
                if(ContientFinal(ntmp))
                    r.finaux.insert(r.nb_etats); //On l'ajoute à la liste des finaux

                //On ajoute l'état à la pile d'état à traiter
                pile.push(ntmp);
                m[ntmp] = r.nb_etats;
                r.nb_etats++;

                //On redimensionne le tableau de transition
                for(trans_t::iterator itt = r.trans.begin(); itt != r.trans.end(); itt++)
                    itt->resize(r.nb_etats);
            }
            if(__DEBUG) cout << "On définit la transition de l'état "  << m[tmp] << " " << tmp << " vers l'état " << m[ntmp] << " " << ntmp << " lors de la lecture de '" << (char)(ASCII_A + i) << "'" << endl;

            //On ajoute la transition de l'état dépilé vers le nouvel état
            r.trans[i][m[tmp]].insert(m[ntmp]);
        }
    }

    //On définie le nombre d'état finaux
    r.nb_finaux = r.finaux.size();

    if(__DEBUG) cout << "Fin de la déterminisation. Nombre d'états totaux : " << r.nb_etats << ". Nombre d'état finaux : " << r.nb_finaux  << endl;

    return (pair<AutoNDE* ,map_t>(p_r,m));
}

/*!
 * \brief AutoNDE::Append On garde x, et on "ajoute" trans et epsilon_clot de y
 * en renommant ses états, id est en décalant les indices des états de y
 * de x.nb_etats
 * \return Un nouvelle automate
 */
AutoNDE* AutoNDE::Append(const AutoNDE& value) const{
    assert(this->nb_symbs == value.nb_symbs);

    AutoNDE* r = new AutoNDE();

    //Définition des des d'états, de symboles et d'états finaux
    r->nb_symbs = this->nb_symbs;
    r->nb_etats = this->nb_etats + value.nb_etats;
    r->nb_finaux = this->nb_finaux + value.nb_finaux;

    //Ajout des epsilon transitions
    for(auto etats:this->epsilon)
        r->epsilon.push_back(etats);
    for(auto etats:value.epsilon)
    {
        etatset_t new_etats;
        for(auto etat:etats)
            new_etats.insert(etat + this->nb_etats);
        r->epsilon.push_back(new_etats);
    }

    //Ajout des transitions
    r->trans.resize(nb_symbs);
    for(etat_t i = 0; i < nb_symbs; i++)
    {
        r->trans[i].resize(this->nb_etats + value.nb_etats);
        for(etat_t j = 0; j < this->nb_etats; j++)
                r->trans[i][j].insert(this->trans[i][j].begin(),this->trans[i][j].end());
        for(etat_t j = 0; j < value.nb_etats; j++)
        {
            for(auto k:value.trans[i][j])
                r->trans[i][j + this->nb_etats].insert(k + this->nb_etats);
        }
    }

    r->Warshall();

    return r;
}

/*!
 * \brief AutoNDE::Union Retourne un nouvel automate contenant l'union de l'automate actuel avec celui passé en argument
 * \return Un nouvel automate
 */
AutoNDE* AutoNDE::Union(const AutoNDE& value) const{
    assert(this->nb_symbs == value.nb_symbs);
    AutoNDE* r = Append(value);

    //Ajout de l'état initial
    r->nb_etats += 1;
    r->epsilon.resize(r->nb_etats);
    r->epsilon[r->nb_etats - 1].insert(initial);
    r->epsilon[r->nb_etats - 1].insert(nb_etats + value.initial);
    r->initial = r->nb_etats - 1;

    //Ajout des états finaux
    for(auto final: this->finaux)
        r->finaux.insert(final);
    for(auto final: value.finaux)
        r->finaux.insert(final + this->nb_etats);

    //Ajout des transitions
    for(auto etats: r->trans)
        etats.resize(r->nb_etats);

    r->Warshall();

    return r;
}

/*!
 * \brief AutoNDE::Concat Retourne un nouvel automate contenant la concaténation de l'automate actuel avec celui passé en argument
 * \return Un nouvel automate
 */
AutoNDE* AutoNDE::Concat(const AutoNDE& value) const{
    assert(this->nb_symbs == value.nb_symbs);
    AutoNDE* r = Append(value);

    //Ajout de l'état initial
    r->initial = initial;
    //Ajout des états finaux
    for(auto etat: value.finaux)
        r->finaux.insert(etat + this->nb_etats);
    //Ajout des transitions
    for(auto etat: this->finaux)
        r->epsilon[etat].insert(value.initial + this->nb_etats);

    r->Warshall();

    return r;
}

/*!
 * \brief AutoNDE::Complement Retourne un nouvel automate contenant le complément de l'automate
 * \return Un nouvel automate
 */
AutoNDE* AutoNDE::Complement() const{
  //On copie l'état actuel dans un nouvel état que l'on retournera
  AutoNDE* r = new AutoNDE(*this);

  //Suppression des états finaux
  r->finaux.clear();
  //Pour chaque état
  for(etat_t i = 0; i < nb_etats; i++)
  {
      //On test si celui-ci est final dans l'automate courant
      etatset_t etat = {i};
      if(!ContientFinal(etat))
          //Si ce n'est pas le cas on l'insert dans
          //le tableau d'état final de l'automate à retourner
          r->finaux.insert(i);
  }

  r->Warshall();

  return r;
}

/*!
 * \brief AutoNDE::Kleene Retourne un nouvel automate contenant l'étoile de Kleen de l'automate
 * \return Un nouvel automate
 */
AutoNDE* AutoNDE::Kleene() const{
     //On copie l'état actuel dans un nouvel état que l'on retournera
    AutoNDE* r = new AutoNDE(*this);

    //Ajout de l'état dans epsilon trans
    etatset_t etats = {r->initial};
    r->epsilon.push_back(etats);
    //Déifinition du nouvel état de départ
    r->initial = r->nb_etats;
    //Ajout des transitions spontannées des états finaux vers le nouvel état de départ
    for(auto etat: r->finaux)
        r->epsilon[etat].insert(r->initial);
    //Ajout d'une transition spontannée du nouvel état de départ vers lui même
    r->epsilon[r->initial].insert(r->initial);
    //Ajout l'état de départ en temps qu'état final
    r->finaux.insert(r->initial);
    //Ajout de l'état dans trans
    r->nb_etats ++;
    for(auto etats: r->trans)
        etats.resize(r->nb_etats);

    r->Warshall();

    return r;
}

/*!
 * \brief AutoNDE::Intersection Retourne un nouvel automate contenant l'intersection de l'automate actuel avec celui passé en argument
 * \return Un nouvel automate
 */
AutoNDE* AutoNDE::Intersection(const AutoNDE& value) const{
    AutoNDE *r, *r1, *r2, *tmp;

    //Complément de r1
    r1 = this->Complement();
    //Complément de r2
    r2 = value.Complement();
    //Union de ~r1 et ~r2
    tmp = r1->Union(*r2);
    //Complément de l'union du complément de ~r1 et ~r2
    r = tmp->Complement();
    //Gestion correcte de la mémoire : on efface la variable intermédiaire initialisé dans le tas.
    delete tmp;

    r->Warshall();

    return r;
}

void Help(ostream& out, char *s){
  out << "Utilisation du programme " << s << " :" << endl ;
  out << "-det ou -determinize Input :\n\t déterminise Input" << endl;
  out << "-cup ou -union Input1 Input2 :\n\t calcule l'union" << endl;
  out << "-cat ou -concat Input1 Input2 :\n\t calcul la concaténation" << endl;
  out << "-star ou -kleene Input :\n\t calcul de A*" << endl;
  out << "-bar ou -complement Input :\n\t calcul du complément" << endl;
  out << "-cap ou -intersection Input1 Input2 :\n\t calcul de l'intersection" << endl;
  out << "-acc ou -accept Input Word:\n\t détermine si le mot Word est accepté" << endl;
  out << "-nop ou -no_operation Input :\n\t ne rien faire de partiulier" << endl;

  out << "-o ou -output Output :\n\t écrire le résultat dans le fichier Output, afficher sur STDOUT si non spécifié" << endl;
  out << "-g ou -graphe :\n\t l'output est au format dot/graphiz" << endl  << endl;

  out << "Exemple '" << s << " -determinize auto.txt -output determin.txt'" << endl;
}

int main(int argc, char* argv[] ){
    if(argc < 3){
        Help(cout, argv[0]);
        return EXIT_FAILURE;
    }

    long pos;
    long act=-1;         // pos et act pour savoir quelle action effectuer
    bool files = false;  // option à un seul ou deux fichiers ?
    string str, in1, in2, out, acc;
    // chaines pour (resp.) tampon; fichier d'entrée Input1; fichier d'entrée Input2;
    // fichier de sortie et chaine dont l'acceptation est à tester
    bool toFile, graphMode;     // sortie STDOUT ou fichier ? Si fichier, format graphviz ?

    // options acceptées
    const size_t NBOPT = 10;
    string aLN[] = {"determinize", "union", "concat", "kleene", "complement", "intersection", "accept", "no_operation", "output", "graph"};
    string aSN[] = {"det", "cup", "cat", "star", "bar", "cap", "acc", "nop", "o", "g"};

    // on essaie de "parser" chaque option de la ligne de commande
    for(int i=1; i<argc; ++i){
        if (__DEBUG) cerr << "argv[" << i << "] = '" << argv[i] << "'" << endl;
        str = argv[i];
        pos = -1;
        string* pL = find(aLN, aLN+NBOPT, str.substr(1));
        string* pS = find(aSN, aSN+NBOPT, str.substr(1));

        if(pL!=aLN+NBOPT)
            pos = pL - aLN;
        if(pS!=aSN+NBOPT)
            pos = pS - aSN;

        if(pos != -1){
            // (pos != -1) <=> on a trouvé une option longue ou courte
            if (__DEBUG) cerr << "Key found (" << pos << ") : " << str << endl;
            switch (pos) {
            case 0: //det
                in1 = argv[++i];
                break;
            case 1: //cup
                in1 = argv[++i];
                in2 = argv[++i];
                files = true;
                break;
            case 2: //cat
                in1 = argv[++i];
                in2 = argv[++i];
                files = true;
                break;
            case 3: //star
                in1 = argv[++i];
                break;
            case 4: //bar
                in1 = argv[++i];
                break;
            case 5: //cap
                in1 = argv[++i];
                in2 = argv[++i];
                files = true;
                break;
            case 6: //acc
                in1 = argv[++i];
                acc = argv[++i];
                break;
            case 7: //nop
                in1 = argv[++i];
                break;
            case 8: //o
                toFile = true;
                out = argv[++i];
                break;
            case 9: //g
                graphMode = true;
                break;
            default:
                return EXIT_FAILURE;
            }
        }
        else{
            cerr << "Option inconnue "<< str << endl;
            return EXIT_FAILURE;
        }

        if(pos<8){
            if(act > -1){
                cerr << "Plusieurs actions spécififées"<< endl;
                return EXIT_FAILURE;
            }
            else
                act = pos;
        }
    }

    if (act == -1){
        cerr << "Pas d'action spécififée"<< endl;
        return EXIT_FAILURE;
    }

    // Les options sont OK, on va essayer de lire le(s) automate(s) at1 (et at2)
    //   et effectuer l'action spécifiée. Atr stockera le résultat

    AutoNDE *at1;
    AutoNDE *at2;
    AutoNDE *atr;
    pair<AutoNDE* ,map_t> d;

    at1 = FromFile(in1);
    if (at1 == nullptr){
        cerr << "Erreur de lecture n°1 " << in1 << endl;
        return EXIT_FAILURE;
    }
    else
        cout << "Affichage de l'automate passé en premier argument : " << endl << endl << *at1 << endl;
    if (files){
        at2 = FromFile(in2);
        if (at2 == nullptr){
            cerr << "Erreur de lecture n°2 " << in2 << endl;
            return EXIT_FAILURE;
        }
        else
            cout << "Affichage de l'automate passé en second argument : " << endl << endl << *at2 << endl;
    }

    switch(act) {
    case 0: //det
        atr = at1->Determinize().first;
        break;
    case 1: //cup
        atr =  at1->Union(*at2);
        break;
    case 2: //cat
        atr =  at1->Concat(*at2);
        break;
    case 3: //star
        atr =  at1->Kleene();
        break;
    case 4: //bar
        atr =  at1->Complement();
        break;
    case 5: //cap
        atr =  at1->Intersection(*at2);
        break;
    case 6: //acc
        cout << "'" << acc << "' est accepté: " << at1->Accept(acc) << endl;
        atr = at1;
        break;
    case 7: //nop
        atr = at1;
        break;
    default:
        return EXIT_FAILURE;
    }
    cout << *atr;
    // on affiche le résultat ou on l'écrit dans un fichier
    if(!toFile)
        // affichage
        cout << *atr;
    else{
        if(graphMode){
            // génération du graphe
            atr->ToGraph(out + ".gv");
            system(("dot -Tpng " + out + ".gv -o " + out + ".png").c_str());
        }
        else{
            // génération du fichier
            ofstream f(out.c_str(), ios::trunc);
            if(f.fail())
                return EXIT_FAILURE;
            f << atr;
        }
    }

    return EXIT_SUCCESS;
}
